package com.wyz.saas.common.audio

import android.annotation.SuppressLint
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.os.SystemClock
import android.util.Log
import net.qiujuer.lame.Lame
import net.qiujuer.lame.LameAsyncEncoder
import net.qiujuer.lame.LameOutputStream
import java.io.*
import kotlin.math.log10

class AudioRecordHelper {
    companion object {
        // 打印日志使用
        private val TAG = AudioRecordHelper::class.java.simpleName

        // 采样频率集合，用于适应不同手机情况
        private val SAMPLE_RATES = intArrayOf(44100, 22050, 11025, 8000)
    }

    /**
     * 录制的回调
     */
    interface AudioRecordListener {
        // 录制开始的回调
        fun onRecordStart()

        // 回调进度，当前的时间
        fun onProgress(time: Long, bufferSize: Double)

        // 录制完成的回调，如果停止录制时传递的是取消，那么则不会回调该方法
        fun onRecordDone(file: File, time: Long)

        fun onRecordCancel()
    }

    // 状态回调
    private val callback: AudioRecordListener

    // 缓存文件，无论那一个录音都复用同一个缓存文件
    private val tmpFile: File

    // 进行初始化时需要的buffer大小, 通过AudioRecord.getMinBufferSize运算得到
    // AudioRecord.getMinBufferSize得到的是bytes的大小；
    // 而我们读取的时候是short
    // 所以需要该值为AudioRecord.getMinBufferSize/2
    private var minShortBufferSize = 0

    // 录制完成
    private var isDone = false

    // 是否取消
    private var isCancel = false

    /**
     * 构造函数
     *
     * @param tmpFile  缓存文件
     * @param callback 录制的状态回调
     */
    constructor(tmpFile: File, callback: AudioRecordListener) {
        this.tmpFile = tmpFile
        this.callback = callback
    }

    /**
     * 初始化一个录音器
     */
    @SuppressLint("MissingPermission")
    private fun initAudioRecord(): AudioRecord? {
        // 遍历采样频率
        for (rate in SAMPLE_RATES) {
            // 编码比特率
            for (audioFormat in shortArrayOf(
                AudioFormat.ENCODING_PCM_16BIT.toShort(),
                AudioFormat.ENCODING_PCM_8BIT.toShort()
            )) {
                // 录音通道：双通道，单通道
                for (channelConfig in shortArrayOf(
                    AudioFormat.CHANNEL_IN_STEREO.toShort(),
                    AudioFormat.CHANNEL_IN_MONO.toShort()
                )) {
                    try {
                        // 尝试获取最小的缓存区间大小
                        val bufferSize = AudioRecord.getMinBufferSize(
                            rate,
                            channelConfig.toInt(),
                            audioFormat.toInt()
                        )
                        if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {
                            // 如果初始化成功
                            val recorder = AudioRecord(
                                MediaRecorder.AudioSource.DEFAULT,
                                rate,
                                channelConfig.toInt(),
                                audioFormat.toInt(),
                                bufferSize
                            )
                            // 尝试进行构建
                            if (recorder.state == AudioRecord.STATE_INITIALIZED) {
                                // 在后面的使用中我们使用的类型是short，得到的是byte类型的缓冲区间大小
                                // 所以是其一般的大小即可，很多人不注意这一点很容易导致多余内存消耗
                                minShortBufferSize = bufferSize / 2
                                return recorder
                            }
                        }
                    } catch (e: Exception) {
                        Log.e(TAG, rate.toString() + "Exception, keep trying.", e)
                    }
                }
            }
        }
        return null
    }

    /**
     * 初始化缓存的文件
     * 文件已经存在则重新进行覆盖新文件
     */
    private fun initTmpFile(): File? {
        if (tmpFile.exists()) {
            tmpFile.delete()
        }
        try {
            if (tmpFile.createNewFile()) return tmpFile
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return null
    }

    /**
     * 进行异步录制
     */
    fun recordAsync() {
        object : Thread() {
            override fun run() {
                record()
            }
        }.start()
    }

    /**
     * 进行同步录制
     * @return 录制完成后返回一个文件，文件就是缓存的文件
     */
    fun record(): File? {
        isCancel = false
        isDone = false

        // 开始进行初始化
        var audioRecorder: AudioRecord
        var file: File
        val initAudioRecord = initAudioRecord()
        val initTmpFile = initTmpFile()
        if (initAudioRecord == null || initTmpFile == null) {
            return null
        }

        audioRecorder = initAudioRecord
        file = initTmpFile

        // 初始化输出到文件的流
        val outputStream: BufferedOutputStream
        try {
            outputStream = BufferedOutputStream(FileOutputStream(file))
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
            return null
        }
        val shortBufferSize = minShortBufferSize
        val callback = callback

        // 初始化Lame转码库相关参数，传入当前的输入采样率，通道，以及输出的mp3格式的采样率
        val lame = Lame(
            audioRecorder.sampleRate,
            audioRecorder.channelCount,
            audioRecorder.sampleRate
        )
        // 构建一个输出流，定向到文件流上面
        val lameOutputStream = LameOutputStream(lame, outputStream, shortBufferSize)
        // 构建一个异步的编码器，这样可以避免阻塞当前线程读取用户的录音
        val lameAsyncEncoder = LameAsyncEncoder(lameOutputStream, shortBufferSize)

        var readSize: Int
        var endTime: Long
        // 通知开始
        audioRecorder.startRecording()
        callback.onRecordStart()
        // 记录开始的时间
        val startTime = SystemClock.uptimeMillis()
        // 在当前线程中循环的读取系统录制的用户音频
        while (true) {
            // 从异步Lame编码器中获取一个缓存的buffer，然后把用户的录音读取到里边
            val buffer = lameAsyncEncoder.freeBuffer
            // 开始进行读取
            readSize = audioRecorder.read(buffer, 0, shortBufferSize)
            var volume: Double = 0.0
            // 如果读取成功
            if (AudioRecord.ERROR_INVALID_OPERATION != readSize) {
                // 那么把读取成功的数据，push到异步转码器Lame中，进行异步的处理
                // 计算分贝
                var a = 0L
                for (idx in buffer.indices) {
                    a += buffer[idx] * buffer[idx]
                }
                val mean = a / readSize.toDouble()
                volume = 10 * log10(mean)
                lameAsyncEncoder.push(buffer, readSize)
            }
            // 回调进度
            endTime = SystemClock.uptimeMillis()
            callback.onProgress(endTime - startTime, volume)

            // 如果没有完成标示则继续录制
            if (isDone) {
                break
            }
        }
        // 进行录制完成
        audioRecorder.stop()
        // 释放录制器
        audioRecorder.release()
        // 当前线程等待异步处理器完成处理
        lameAsyncEncoder.awaitEnd()
        // 如果说不是取消，则通知回调
        if (!isCancel) {
            callback.onRecordDone(file, endTime - startTime)
        }else{
            callback.onRecordCancel()
        }
        // 返回文件
        return file
    }

    /**
     * 停止录制语音
     * 传递一个参数标示是取消录音还是完成录音
     * @param isCancel True 则代表想要取消录音；False 则代表正常完成录音
     */
    fun stop(isCancel: Boolean) {
        this.isCancel = isCancel
        isDone = true
    }

}